using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Threading;

namespace PSE.Hosting.ServiceHoster.Configuration
{
    public class ConfigurationFile
    {
        #region Instance

        private static ConfigurationFile _instance;

        public static ConfigurationFile Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = new ConfigurationFile();
                }

                return _instance;
            }
        }

        #endregion

        #region Public methods

        public List<PackageElement> GetPackages()
        {
            System.Configuration.Configuration config = CreateConfiguration();
            ServiceHosterSection hosterSection = CreateHosterSection(config);

            List<PackageElement> returnList = new List<PackageElement>();

            foreach (PackageConfigurationElement package in hosterSection.Packages)
            {
                PackageElement packageElement = new PackageElement();
                packageElement.Name = package.Name;
                packageElement.IsStarted = package.IsStarted;
                packageElement.Domain = package.Domain;
                packageElement.UserName = package.UserName;
                packageElement.Password = package.Password;

                returnList.Add(packageElement);
            }

            return returnList;
        }

        public List<AssemblyElement> GetAssemblies(string packageName)
        {
            System.Configuration.Configuration config = CreateConfiguration();
            ServiceHosterSection hosterSection = CreateHosterSection(config);

            List<AssemblyElement> returnList = new List<AssemblyElement>();

            PackageConfigurationElement package = hosterSection.Packages[packageName];
            AssembliesCollection assemblies = package.Assemblies;

            foreach (AssemblyConfigurationElement element in assemblies)
            {
                AssemblyElement assemblyElement = new AssemblyElement();
                assemblyElement.FullName = element.FullName;
                assemblyElement.CodeBase = element.CodeBase;

                returnList.Add(assemblyElement);
            }

            return returnList;
        }

        public AssemblyElement GetAssembly(string packageName, string fileName)
        {
            AssemblyElement assemblyElement = new AssemblyElement();

            System.Configuration.Configuration config = CreateConfiguration();
            ServiceHosterSection hosterSection = CreateHosterSection(config);

            List<AssemblyElement> returnList = new List<AssemblyElement>();

            AssemblyConfigurationElement assembly = hosterSection.Packages[packageName].Assemblies[fileName];

            if (assembly == null)
            {
                throw new InvalidAssemblyException("The specified assembly is not added to BM&FBOVESPA WCF Services.");
            }
            else
            {
                assemblyElement.FullName = assembly.FullName;
                assemblyElement.CodeBase = assembly.CodeBase;
            }

            return assemblyElement;
        }

        public void AddPackage(string packageName, string domain, string userName, string password)
        {
            System.Configuration.Configuration config = CreateConfiguration();
            ServiceHosterSection hosterSection = CreateHosterSection(config);

            PackageConfigurationElement package = new PackageConfigurationElement(packageName, domain, userName, password);
            hosterSection.Packages.Add(package);
            config.Save(ConfigurationSaveMode.Modified);
        }

        public void UpdatePackage(string packageName, string domain, string userName, string password)
        {
            System.Configuration.Configuration config = CreateConfiguration();
            ServiceHosterSection hosterSection = CreateHosterSection(config);

            hosterSection.Packages[packageName].Domain = domain;
            hosterSection.Packages[packageName].UserName = userName;
            hosterSection.Packages[packageName].Password = password;

            config.Save(ConfigurationSaveMode.Modified);
        }

        public void RemovePackage(string packageName)
        {
            System.Configuration.Configuration config = CreateConfiguration();
            ServiceHosterSection hosterSection = CreateHosterSection(config);

            hosterSection.Packages.Remove(packageName);
            config.Save(ConfigurationSaveMode.Modified);
        }

        public void SavePackageStarted(string packageName)
        {
            System.Configuration.Configuration config = CreateConfiguration();
            ServiceHosterSection hosterSection = CreateHosterSection(config);

            hosterSection.Packages[packageName].IsStarted = true;

            config.Save(ConfigurationSaveMode.Modified);
        }

        public void SavePackageStoped(string packageName)
        {
            System.Configuration.Configuration config = CreateConfiguration();
            ServiceHosterSection hosterSection = CreateHosterSection(config);

            hosterSection.Packages[packageName].IsStarted = false;

            config.Save(ConfigurationSaveMode.Modified);
        }

        public void AddAssembly(string packageName, string codeBase)
        {
            if (Path.GetExtension(codeBase) != ".dll")
                throw new PSE.Hosting.ServiceHoster.Configuration.InvalidAssemblyException(string.Format("Invalid assembly '{0}'", codeBase));

            System.Configuration.Configuration config = CreateConfiguration();
            ServiceHosterSection hosterSection = CreateHosterSection(config);

            // Saves the assembly information in the configuration file.
            string assemblyFullName = "";

            string fileName = Path.GetFileName(codeBase);

            PackageConfigurationElement packageConfig = hosterSection.Packages[packageName];

            AssemblyConfigurationElement serviceConfigElement = new AssemblyConfigurationElement(fileName, codeBase, assemblyFullName);

            packageConfig.Assemblies.Add(serviceConfigElement);

            config.Save(ConfigurationSaveMode.Modified);
        }

        public void RemoveAssembly(string packageName, string assemblyFullPath)
        {
            System.Configuration.Configuration config = CreateConfiguration();
            ServiceHosterSection hosterSection = CreateHosterSection(config);

            string fileName = Path.GetFileName(assemblyFullPath);

            PackageConfigurationElement packageConfig = hosterSection.Packages[packageName];
            packageConfig.Assemblies.Remove(fileName);

            config.Save(ConfigurationSaveMode.Modified);
        }

        #endregion

        #region Private methods

        private System.Configuration.Configuration CreateConfiguration()
        {
            string fileName = Environment.GetEnvironmentVariable(Constants.AllUsersEnvironmentVariable) +
                                Constants.ServiceHosterConfigPath +
                                Constants.FileName;

            ExeConfigurationFileMap map = new ExeConfigurationFileMap();
            map.ExeConfigFilename = fileName;

            return ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None);
        }

        private ServiceHosterSection CreateHosterSection(System.Configuration.Configuration config)
        {
            ServiceHosterSection hosterSection = null;

            try
            {
                hosterSection = (ServiceHosterSection)config.GetSection(Constants.ServiceHosterSection);
            }
            catch (ConfigurationErrorsException ex)
            {
                throw new ConfigurationErrorsException(ex.Message, ex);
            }

            if (hosterSection == null)
            {
                hosterSection = new ServiceHosterSection();
                config.Sections.Add(Constants.ServiceHosterSection, hosterSection);
                config.Save(ConfigurationSaveMode.Minimal);
            }

            hosterSection.SectionInformation.ProtectSection(Constants.ProtectionConfigurationProvider);
            return hosterSection;
        }

        #endregion
    }
}
